﻿using System.Windows;
using System;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.IO;
using Prism.Events;
using System.Windows.Media.Animation;
using Prism.Commands;
using System.Windows.Media.Effects;
using Prism.Ioc;
using Module.Common.Common;
using Module.Common.PubEvent;
using Module.Common.Common.QiPuHelper;
using System.Diagnostics;

namespace Module.Common.Views
{
    /// <summary>
    /// 棋子类后台代码
    /// </summary>
    public partial class ChessPieces : UserControl
    {
        public int PiecesId { get; set; }  // 棋子编号
        private readonly IEventAggregator Ea;

        public bool Selected { get; set; }   // 棋子选中状态


        /// <summary>
        /// 棋子初始位置，非残局和残局设计时使用
        /// </summary>
        private static readonly int[,] qiZiInitPosition = new int[32, 2]
        {
            {4, 0},{3, 0},{5, 0},{2, 0},{6, 0},{1, 0},{7, 0},{0, 0},{8, 0},
            {1, 2},{7, 2},
            {0, 3},{2, 3},{4, 3},{6, 3},{8, 3},
            {4, 9},{3, 9},{5, 9},{2, 9},{6, 9},{1, 9},{7, 9},{0, 9},{8, 9},
            {1, 7},{7, 7},
            {0, 6},{2, 6},{4, 6},{6, 6},{8, 6}
        };


        private int init_col;  // 开局时棋子的列坐标
        private int init_row;  // 开局时棋子的行坐标
        private int OldCol;
        private int OldRow;
        public int CurCol { get; set; }  // 棋子的列坐标
        public int CurRow { get; set; }  // 棋子的行坐标
        public int NewCol;
        public int NewRow;


        private bool _moveLawful; // 走棋合法性
        public string ModuleName { get; set; } // 模块名称

        private bool _isQiPanFanZhuan; // 棋盘翻转状态
        public bool IsQiPanFanZhuan { get { return _isQiPanFanZhuan; } set { _isQiPanFanZhuan = value; } }

        /// <summary>
        /// 棋子类<see cref="ChessPieces"/>构造函数。
        /// 生成棋子后，
        /// 1、需调用<see cref="SetID"/>设定棋子编号，才能载入对应的棋子图像;
        /// 2、需调用<see cref="SetDefaultInitPosition"/>或者<see cref="SetAssignInitPosition"/>设定棋子在棋盘上的初始位置
        /// </summary>
        public ChessPieces(IEventAggregator ea)
        {
            InitializeComponent();
            this.Ea = ea;
            Ea.GetEvent<SystemMessageEvent>().Subscribe(SystemMessageProcess);
            Ea.GetEvent<ServiceMessageEvent>().Subscribe(
                ServiceMessageProcess,
                ThreadOption.PublisherThread,
                false,
                filter => Visibility == Visibility.Visible && filter.ModuleName == this.ModuleName
                );
            Ea.GetEvent<ModuleMessageEvent>().Subscribe(
                ModuleMessageProcess,
                ThreadOption.PublisherThread,
                false,
                filter => filter.ModuleName == this.ModuleName // && Visibility==Visibility.Visible 此过滤条件不能用，因为在悔棋时，已被吃的棋子要恢复，但它收不到信息。
                );
        }
        #region 订阅消息处理
        private void ServiceMessageProcess(ServiceMessage obj)
        {
            switch (obj.MessageName)
            {
                case "hasJiangjunORJueshaORKunbi":
                    if (obj.jiangJun_Message[0] == 0 && PiecesId < 16 || obj.jiangJun_Message[0] == 16 && PiecesId >= 16)
                    {
                        // 如果本方正被将军，且本棋子移动后仍然被将军，则本次移动是非法的，系统自动取消本次移动
                        _moveLawful = false;
                    }
                    else
                    {
                        _moveLawful = true;
                    }
                    break;
                case "noJiangjunORJueshaORKunbi":
                    _moveLawful = true;
                    break;
                case "BestMoveOk": // 象棋引擎推荐善法计算完毕
                    StepData data = obj.BestStepData;
                    if (data != null && data.QiZiId < 16 && data.QiZiId == PiecesId)  // 黑方自动走棋
                    {
                        GlobalFunction.Delay(1000);
                        Dispatcher.Invoke(() =>    // 解决线程冲突问题
                        {
                            PieceMoveAnimation(data.X1, data.Y1); 
                        });
                    }
                    break;
                default: break;
            }
        }

        private void SystemMessageProcess(SystemMessage obj)
        {
            switch (obj.MessageName)
            {
                
                case "qipanInverse":    // 棋盘翻转消息
                    IsQiPanFanZhuan = !IsQiPanFanZhuan;
                    SetPosition(CurCol, CurRow);
                    break;
                default:
                    break;
            }
        }
        /// <summary>
        /// 订阅消息处理
        /// </summary>
        /// <param name="obj">消息体</param>
        private void ModuleMessageProcess(ModuleMessage obj)
        {
            switch (obj.MessageName)
            {
                case "qiziGetClicked":   // 有棋子被点击消息
                    if (IsQiPanFanZhuan) Panel.SetZIndex(this, 9 - CurRow); else Panel.SetZIndex(this, CurRow); // 修改显示顺序
                    Selected = false;
                    break;
                case "pointGetClicked":  // 有移动点被点击消息
                    if (Selected) { PieceMoveAnimation(obj.NewCol, obj.NewRow); }

                    break;
                case "moveSideError":
                    _moveLawful = false;
                    break;
                case "goBackStepLunch":
                    if (PiecesId == obj.Id)
                    {
                        SetPosition(obj.BufferCol, obj.BufferRow);
                        OldCol = obj.NewCol;
                        OldRow = obj.NewRow;
                        CurCol = obj.BufferCol;
                        CurRow = obj.BufferRow;
                    }
                    if (PiecesId == obj.DieQz) { Visibility = Visibility.Visible; }
                    break;
                case "reStart": // 重新开局
                    _moveLawful = true;

                    ShowOnInitPosition();
                    break;
                default: break;
            }
        }

        #endregion

        #region 棋子初始化，设置编号和初始位置
        public void SetID(int id)
        {
            if (id is < 0 or > 31)
            {
                return;
            }
            PiecesId = id;
            string path = Environment.CurrentDirectory + @"\Resource\picture\" + GlobalData.QiZiImageFileName(PiecesId) + ".png";
            //string path = @"pack://application:,,,/picture/" + GlobalValue.QiZiImageFileName[QiziId] + ".png";
            if (!File.Exists(path))
            {
                MessageBox.Show($"未找到文件：{path}");
                return;
            }
            BitmapImage bi = new(new Uri(path, UriKind.Absolute)); // 载入棋子图片
            bi.Freeze();
            QiZiImage.Source = bi;
            //SetDefaultInitPosition();
        }

        /// <summary>
        /// 棋子显示在初始位置
        /// </summary>
        public void ShowOnInitPosition()
        {
            if (init_col < 0 || init_row < 0) return;

            CurCol = init_col; OldCol = init_col;
            CurRow = init_row; OldRow = init_row;
            int x = CurCol, y = CurRow;
            if (IsQiPanFanZhuan) // 如果棋盘翻转为上红下黑，则进行坐标转换
            {
                x = 8 - CurCol;
                y = 9 - CurRow;
            }
            SetValue(Canvas.LeftProperty, GlobalData.GetQiPanGrid_X(x));
            SetValue(Canvas.TopProperty, GlobalData.GetQiPanGrid_Y(y));
            if (IsQiPanFanZhuan) Panel.SetZIndex(this, 9 - CurRow); else Panel.SetZIndex(this, CurRow);
            Visibility = Visibility.Visible;
        }

        /// <summary>
        /// 设置为默认初始位置
        /// </summary>
        public void SetDefaultInitPosition()
        {
            init_col = qiZiInitPosition[PiecesId, 0]; // 棋子的默认位置
            init_row = qiZiInitPosition[PiecesId, 1];
            ShowOnInitPosition();
        }

        /// <summary>
        /// 设置为指定初始位置
        /// </summary>
        /// <param name="col"></param>
        /// <param name="row"></param>
        public void SetAssignInitPosition(int col, int row)
        {
            init_col = col;
            init_row = row;
            ShowOnInitPosition();
        }
        #endregion

        /// <summary>
        /// 棋子移动动画
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        public void PieceMoveAnimation(int x, int y)
        {
            NewCol = x;
            NewRow = y;
            if (IsQiPanFanZhuan) // 如果棋盘翻转为上红下黑，则进行坐标转换
            {
                x = 8 - x;
                y = 9 - y;
            }
            // 棋子开始移动前就发出信息，获取相关数据，判断本次移动是否合法。
            // 动画耗时时间长，足够完成数据计算。
            Ea.GetEvent<ModuleMessageEvent>().Publish(new ModuleMessage()
            {
                MessageName = "qiziStartMove",
                Id = PiecesId,
                BufferCol = CurCol,
                BufferRow = CurRow,
                NewCol = NewCol,
                NewRow = NewRow,
                SenderFrom = this.GetType().Name,
                Content = $"【{this.GetType().Name}】：【{GlobalData.QiZiImageFileName(PiecesId)}】开始移动，请预测棋盘变化，并检查走子是否合法",
                ModuleName = this.ModuleName,
            });

            #region 棋子移动动画
            // xaml中，动画参数只能是固定数据，而移动位置是个变量，所以只能在后台代码中设置移动动画
            DoubleAnimation dax = new()
            {
                To = GlobalData.GetQiPanGrid_X(x),
                Duration = TimeSpan.FromMilliseconds(300),
            };
            Storyboard.SetTargetName(dax, this.Name);
            Storyboard.SetTargetProperty(dax, new PropertyPath(Canvas.LeftProperty));

            DoubleAnimation day = new()
            {
                To = GlobalData.GetQiPanGrid_Y(y),
                Duration = TimeSpan.FromMilliseconds(300),
            };
            Storyboard.SetTargetName(day, this.Name);
            Storyboard.SetTargetProperty(day, new PropertyPath(Canvas.TopProperty));

            Storyboard stb = new()
            {
                // 此处不使用HoldEnd，而使用Stop，其目的：
                // 一是为了解决动画之后无法再次使用动画改变控件属性的问题；
                // 二是棋子移动动作是否合法尚未可知，只能默认为不合法，使用Stop可在动画完成后，控件返回原位置。
                FillBehavior = FillBehavior.Stop
            };
            stb.Children.Add(dax);
            stb.Children.Add(day);
            stb.Completed += QiziMoveComplete;
            stb.Begin(this); // 添加this参数，很巧妙。

            #endregion

        }
        
        /// <summary>
        /// 改变棋子的坐标位置。
        /// 棋盘上设置了9列10行的坐标系，左上角第一个位置的坐标为（0，0），右下角最后一个位置的坐标为（8，9）
        /// </summary>
        /// <param name="x">行</param>
        /// <param name="y">列</param>
        public void SetPosition(int x, int y)
        {
            CurCol = x;
            CurRow = y;
            if (IsQiPanFanZhuan) // 如果棋盘翻转为上红下黑，则进行坐标转换
            {
                x = 8 - x;
                y = 9 - y;
            }
            SetValue(Canvas.LeftProperty, GlobalData.GetQiPanGrid_X(x));
            SetValue(Canvas.TopProperty, GlobalData.GetQiPanGrid_Y(y));
            Panel.SetZIndex(this, y);
        }

        /// <summary>
        /// 棋子移动动画运行完毕后
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void QiziMoveComplete(object sender, EventArgs e)
        {
            Selected = false;
            Ea.GetEvent<ModuleMessageEvent>().Publish(new ModuleMessage()
            {
                MessageName = "qiziMoveAnimationCompleted",
                Id = PiecesId,
                BufferCol = CurCol,
                BufferRow = CurRow,
                NewCol = NewCol,
                NewRow = NewRow,
                SenderFrom = this.GetType().Name,
                Content = $"【{this.GetType().Name}】：【{GlobalData.QiZiImageFileName(PiecesId)}】移动动画执行完毕",
                ModuleName = this.ModuleName,
            });
            if (_moveLawful) // 如果移动合法，则确认移动到该位置
            {
                OldCol = CurCol;
                OldRow = CurRow;
                Ea.GetEvent<ModuleMessageEvent>().Publish(new ModuleMessage()
                {
                    MessageName = "qiziLocationHasChanged", // 发出棋子位置已发生改变的消息，用于棋谱模块订阅消息并更新棋谱记录。同时，通知目标位置的棋子，你被吃了。
                    Id = PiecesId,
                    BufferCol = CurCol,
                    BufferRow = CurRow,
                    NewCol = NewCol,
                    NewRow = NewRow,
                    SenderFrom = this.GetType().Name,
                    Content = $"【{this.GetType().Name}】：【{GlobalData.QiZiImageFileName(PiecesId)}】的位置发生了变动",
                    ModuleName = this.ModuleName,
                });
                SetPosition(CurCol, CurRow);// 因为设置了FillBehavior=Stop，动画结束后自动回到原位置，因此，需再设置一下最终值。注意，此行只能放在最后，否则棋子会自己把自己吃掉。
            }
            else
            {
                CurCol = OldCol; CurRow = OldRow; // 如果移动不合法，则恢复到原位置
            }
            if (IsQiPanFanZhuan) Panel.SetZIndex(this, 9 - CurRow); else Panel.SetZIndex(this, CurRow);

        }

        /// <summary>
        /// 点击棋子时
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnClick(object sender, RoutedEventArgs e)
        {
            PieceClick();
        }
        public void PieceClick()
        {
            Ea.GetEvent<ModuleMessageEvent>().Publish(new ModuleMessage()
            {
                MessageName = "preQiziGetClicked",
                SenderFrom = this.GetType().Name,
                Content = $"【{this.GetType().Name}】：=======",
                ModuleName = this.ModuleName,
            });
            Ea.GetEvent<ModuleMessageEvent>().Publish(new ModuleMessage()
            {
                MessageName = "qiziGetClicked",
                Id = this.PiecesId,
                NewCol = CurCol,
                NewRow = CurRow,
                SenderFrom = this.GetType().Name,
                Content = $"【{this.GetType().Name}】：【{GlobalData.QiZiImageFileName(PiecesId)}】被选中",
                ModuleName = this.ModuleName,
            });
            Selected = true;
            Panel.SetZIndex(this, 10); // 被选中时，设置本棋子显示顺序高于其他棋子。其他棋子为1。
        }
    }
}
